using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using SharpMud.Text.Dynamic;
using SharpMud.Text.Dynamic.Processing;
using SharpMud.Text.Dynamic.Metadata;

namespace SharpMud.Text.Dynamic.Compilation
{
    public class BuildManager
    {
        #region Private Variables
        private Dictionary<System.Guid, ICompiledDynamicTextBody> _PreparedInstancesByGuid;
        private CompilerParameters _CompilerParameters;
        private CodeDomProvider _DefaultCodeProvider;
        #endregion

        #region Constructors
        private BuildManager()
        {
            _PreparedInstancesByGuid = new Dictionary<Guid, ICompiledDynamicTextBody>();
        }
        public BuildManager(CompilerParameters compilerParams)
            :this()
        {
            SetCompilerParameters(compilerParams);
        }
        public BuildManager(CompilerParameters compilerParams, CodeDomProvider defaultCodeProvider)
            : this(compilerParams)
        {
            if (defaultCodeProvider == null)
                throw new ArgumentNullException("defaultCodeProvider");
            _DefaultCodeProvider = defaultCodeProvider;
        }
        #endregion

        #region Static Stuff
        public static void InsureReference(System.CodeDom.Compiler.CompilerParameters compilerParameters, System.Reflection.Assembly assembly)
        {
            string assRef = System.Uri.UnescapeDataString((new System.Uri(assembly.GetName().CodeBase)).AbsolutePath);
            if (compilerParameters.ReferencedAssemblies.Contains(assRef) == false)
            {
                compilerParameters.ReferencedAssemblies.Add(assRef);
            }
        }

        private static System.Type FindType(IProcessedDynamicTextBody processedDTB, System.Reflection.Assembly ass)
        {
            foreach (System.Type t in ass.GetTypes())
            {
                if (typeof(ICompiledDynamicTextBody).IsAssignableFrom(t))
                {
                    foreach (SourceHashGuidAttribute shga in t.GetCustomAttributes(typeof(SourceHashGuidAttribute), true))
                    {
                        if (shga.SourceHashGuid == processedDTB.SourceHash.ToGUID())
                            return t;
                    }
                }
            }
            return null;
        }
        #endregion

        public void SetCompilerParameters(CompilerParameters compilerParams)
        {
            if(compilerParams==null)
                throw new ArgumentNullException("compilerParams");

            _CompilerParameters = compilerParams;
            InsureReference(_CompilerParameters, typeof(ICompiledDynamicTextBody).Assembly);
            InsureReference(_CompilerParameters, typeof(SharpMud.DynamicTyping.AssembliedTypeLocation).Assembly);
            _CompilerParameters.GenerateExecutable = false;
            _CompilerParameters.GenerateInMemory = true;
        }


        #region Explicit Compile
        public CompilerResults Compile(ISimpleDynamicTextBody unprocessedDTB)
        {
            if (unprocessedDTB.CodeDomProvider != null)
                return Compile(DynamicTextProcessor.Process(unprocessedDTB, unprocessedDTB.CodeDomProvider));
            else
                return Compile(DynamicTextProcessor.Process(unprocessedDTB, _DefaultCodeProvider));
        }
        public CompilerResults Compile(IProcessedDynamicTextBody pDTB)
        {
            if(_PreparedInstancesByGuid.ContainsKey(pDTB.SourceHash.ToGUID()))
                return null;
            if (pDTB.CodeDomProvider==null && _DefaultCodeProvider==null)
                throw new ArgumentException("The given IProcessedDynamicTextBody is not providing an instance of CodeDomProvider.","pDTB.CodeDomProvider");

            CompilerResults results;

            if (pDTB.CodeDomProvider != null)
                results = pDTB.CodeDomProvider.CompileAssemblyFromDom(this._CompilerParameters, pDTB.ToCodeCompileUnit().CodeObject);
            else
                results = _DefaultCodeProvider.CompileAssemblyFromDom(this._CompilerParameters, pDTB.ToCodeCompileUnit().CodeObject);


            if (!results.Errors.HasErrors)
            {
                System.Type t = FindType(pDTB, results.CompiledAssembly);
                if (t != null)
                {
                    ICompiledDynamicTextBody tI = (ICompiledDynamicTextBody)(t.Assembly.CreateInstance(t.Name, true));
                    _PreparedInstancesByGuid.Add(pDTB.SourceHash.ToGUID(), tI);
                }
            }
            return results;
        }
        #endregion

        #region Indirect Compilation
        public ICompiledDynamicTextBody GetHandle(IProcessedDynamicTextBody pDTB)
        {
            if (_PreparedInstancesByGuid.ContainsKey(pDTB.SourceHash.ToGUID()))
            {
                return _PreparedInstancesByGuid[pDTB.SourceHash.ToGUID()];
            }
            return null;
        }
        public ICompiledDynamicTextBody GetHandle(ISimpleDynamicTextBody unprocessedDTB)
        {
            if (unprocessedDTB.CodeDomProvider != null)
                return GetHandle(DynamicTextProcessor.Process(unprocessedDTB, unprocessedDTB.CodeDomProvider));
            else
                return GetHandle(DynamicTextProcessor.Process(unprocessedDTB, _DefaultCodeProvider));
        }
        #endregion
    }
}
